#ifndef PILO_T_SIMPLE_DYNAMIC_ARRAY_H
#define PILO_T_SIMPLE_DYNAMIC_ARRAY_H

#include "common/common.h"
#include "memory/memory.h"
#include "common/exceptions.h"

namespace PILO {
    namespace Core {
        namespace Containers {

                /**
                 * expandable array, you need give it a initial capacity
                 * can bind to a array which in the stack
                 * requirement for element:
                 *   1. element can be copied by memcpy
                 *   2. no extra operation is needed when element is destroyed.
                 *
                 *  suitable for simple data type
                */
            template <typename T, size_t initCapacity=32, size_t inflateStep=16> 
            class TCSimpleDynamicArray
            {
            public:
	            typedef T value_type;

            protected:
	            BOOL _memoryOwner;
	            size_t _size;
	            size_t _capacity;
	            value_type* _data;
             

            public:
	            TCSimpleDynamicArray()
		            : _memoryOwner(TRUE)
		            , _size(0)
		            , _capacity(0)
		            , _data(NULL)
	            {
		            ASSERT(_is_simple_type_<T>::value);
	            }
	            TCSimpleDynamicArray(value_type* buffer, size_t capacity, size_t size)
		            : _memoryOwner(FALSE)
		            , _data(buffer)
		            , _capacity(capacity)
		            , _size(size)
	            {
                    ASSERT(_size != MC_INVALID_SIZE);
                    ASSERT(_capacity != MC_INVALID_SIZE);
		            ASSERT(_is_simple_type_<T>::value);
	            }
	            TCSimpleDynamicArray(const TCSimpleDynamicArray& o)
		            :_memoryOwner(TRUE)
		            ,_size(0)
		            ,_capacity(0)
		            ,_data(NULL)
	            {
		            ASSERT(_is_simple_type_<T>::value);
		            if(o._size == 0) return;
		            if (!this->inflate(o._size)) {
                        throw MAKE_SYSERR(EC_INSUFFICIENT_MEMORY);
                    }
		            memcpy(_data, o._data, o._size*sizeof(value_type));
		            _size = o._size;
	            }
	            ~TCSimpleDynamicArray()
	            {
		            this->destroy();
	            }
	            TCSimpleDynamicArray& operator= (const TCSimpleDynamicArray& o)
	            {
		            if(this == &o) return *this;

		            this->clear();
		            if(o._size<1) return *this;
            		
		            if(_capacity < o._size) {
                        if (! this->inflate(o._size)) {
                            throw MAKE_SYSERR(EC_INSUFFICIENT_MEMORY);
                        }
                    }
		            memcpy(_data, o._data, o._size*sizeof(value_type));
		            _size = o._size;
            		
		            return *this;
	            }

                const value_type * constInternalData() const 
                {
                    return _data;
                }

	            size_t size() const
	            {
		            return _size;
	            }

                size_t count() const
                {
                    return _size;
                }

                size_t capacity() const
                {
                    return _capacity;
                }

                BOOL isMemoryOwner() const
                {
                    return _memoryOwner;
                }

	            void clear()
	            {
		            _size = 0;
	            }
	            BOOL isEmpty() const
	            {
		            return _size < 1;
	            }
	            TCSimpleDynamicArray& attach(value_type* vals, size_t capacity, size_t size)
	            {
                    ASSERT(size != MC_INVALID_SIZE);
                    ASSERT(capacity != MC_INVALID_SIZE);

		            this->destroy();
		            _memoryOwner = FALSE;
		            _size = size;
		            _capacity = capacity;
		            _data = vals;
		            return *this;
	            }
	            void detach()
	            {
		            if(_memoryOwner) return;
		            _memoryOwner = TRUE;
		            _size = 0;
		            _capacity = 0;
		            _data = NULL;
	            }

                value_type * exist(size_t idx)
                {
                    if ((idx >= _size)) {
                        return NULL;
                    }
                    return &(_data[idx]);
                }

                size_t indexOf(const value_type& val, size_t from=0, size_t number=MC_INVALID_SIZE) const
                {
                    ASSERT(from != MC_INVALID_SIZE);

                    size_t to;
                    if (number == MC_INVALID_SIZE) {
                        to = _size;
                    } else {
                        to = from + number;
                        if (to >_size) {
                            to = _size;
                        }
                    }

                    for (size_t i=from; i<to; i++) {
                        if (_data[i] == val) {
                            return i;
                        }
                    }
                    return MC_INVALID_SIZE;
                }

                size_t lastIndexOf(const value_type& val, size_t from=MC_INVALID_SIZE, size_t number=MC_INVALID_SIZE) const
                {
                    size_t fr;
                    if (from == MC_INVALID_SIZE) {
                        fr = _size - 1;
                    } else {
                        fr = from;
                        if  (fr > _size - 1) {
                            fr = _size - 1;
                        }
                    }

                    size_t countElementToSearch;
                    if (number == MC_INVALID_SIZE) {
                        countElementToSearch = fr + 1;
                    } else {
                        if (from < number) {
                            countElementToSearch = fr + 1;
                        } else {
                            countElementToSearch = number;
                        }
                    }

                    while(countElementToSearch > 0) {
                        ASSERT(fr>=0); 
                        if (_data[fr] == val) {
                            return fr;
                        }
                        fr --;  
                        countElementToSearch --;
                    }

                    return MC_INVALID_SIZE;
                }

	            const value_type& at(size_t idx) const
	            {
		            ASSERT(idx < _size);
		            return _data[idx];
	            }

                const value_type& operator [] (size_t idx) const
                {
                    return this->at(idx);
                }

	            value_type& operator [] (size_t idx)
	            {
                    ASSERT(idx != MC_INVALID_SIZE);

                    size_t size = idx + 1;
                    if(_capacity < size) {
                        if (! this->inflate(size)) {
                            throw MAKE_SYSERR(EC_INSUFFICIENT_MEMORY);
                        }
                    }
                    if(_size < size)
                    {
                        this->fillDefault(_size, size-_size);
                        _size = size;
                    }
                    return _data[idx];
	            }
            	
	            TCSimpleDynamicArray& append(const value_type& val)
	            {
                    if(_capacity <= _size) {
                        if (! this->inflate(_size+1)) {
                            throw MAKE_SYSERR(EC_INSUFFICIENT_MEMORY);
                        }
                    }
		            (*this)[_size] = val;
		            return *this;
	            }

                TCSimpleDynamicArray& preappend(const value_type& val)
                {
                    return this->insert(0, val);
                }

                value_type takeLast()
	            {
		            ASSERT(_size>0);
		            _size -= 1;
		            return _data[_size];
	            }

                value_type takeFirst()
                {
                    ASSERT(_size>0);

                    value_type retVal = _data[0];
                    this->removeAt(0);
                    return retVal;
                }

                value_type takeAt(size_t idx)
                {
                    ASSERT(idx<_size);
                    value_type retVal = _data[idx];
                    this->removeAt(0);
                    return retVal;
                }

	            TCSimpleDynamicArray& removeAt(size_t idx)
	            {
                    ASSERT(idx != MC_INVALID_SIZE);

		            if(idx >= _size) {
                        return *this;
                    }

		            size_t len = (_size-idx-1) * sizeof(value_type);
		            if(len > 0)
		            {
			            void* pSrc = &_data[idx+1];
			            void* pDst = &_data[idx];
			            ::memmove(pDst, pSrc, len);
		            }
		            _size -= 1;
		            return *this;
	            }

                BOOL removeOne(const value_type& val)
                {
                    size_t idx = this->indexOf(val, 0, _size);
                    if (MC_INVALID_SIZE == idx) {
                        return FALSE;
                    }
                    this->removeAt(idx);
                    return TRUE;
                }

                size_t removeAll(const value_type& val, size_t maxRemovedCount=MC_INVALID_SIZE)
                {
                    size_t removedCount = 0;

                    while((removedCount<maxRemovedCount) && (this->removeOne(val))) {
                        removedCount ++;
                    }

                    return removedCount;
                }

	            TCSimpleDynamicArray& insert(size_t idx, const value_type& val)
	            {
                    ASSERT(idx != MC_INVALID_SIZE);

		            if(idx >= _size)
		            {
			            (*this)[idx] = val;
			            return *this;
		            }

		            size_t size = _size + 1;
		            if(_capacity < size) {
                        if (! this->inflate(size)) {
                            throw MAKE_SYSERR(EC_INSUFFICIENT_MEMORY);
                        }
                    }

		            size_t len = (_size-idx)*sizeof(value_type);
		            if(len > 0)
		            {
			            void* pSrc = &_data[idx];
			            void* pDst = &_data[idx+1];
			            memmove(pDst, pSrc, len);
		            }
		            _data[idx] = val;
		            _size += 1;
		            return *this;
	            }
              
            protected:
	            // called when capacity lower than size
	            BOOL inflate(size_t size)
	            {
		            ASSERT(_memoryOwner);
                    ASSERT(inflateStep>0);
		            if(_data == MC_NULLPTR_OF_(value_type*))
		            {
			            size_t capacity = initCapacity;
			            while(capacity < size) capacity += inflateStep;
			            _data = (value_type*)MemoryAlloc(capacity*sizeof(value_type));
                        if (_data == MC_NULLPTR_OF_(value_type*)) {
                            return FALSE;
                        }
			            _capacity = capacity;
		            }
		            else
		            {
			            size_t capacity = _capacity;
			            while(capacity < size) capacity += inflateStep;
			            value_type * tmpData = (value_type*)MemoryRealloc(_data, capacity*sizeof(value_type));
                        if (tmpData == MC_NULLPTR_OF_(value_type*)) {
                            return FALSE;
                        }
                        _data = tmpData;
                        _capacity = capacity;
		            }

		            return TRUE;
	            }
	            void destroy()
	            {
		            this->clear();
		            if(_memoryOwner && _data)
		            {
			            MemoryFree(_data);
			            _data = NULL;
			            _capacity = 0;
		            }
	            }

	            void fillDefault(size_t from, size_t count)
	            {
		            const value_type defVal = _type_default_value_<value_type>::get();
		            while(count > 0)
		            {
			            _data[from++] = defVal;
			            count -= 1;
		            }
	            }
            };



        } /* Containers */
    } /* Core */
} /* PILO */














#endif
